#include <time.h>
#include <stdio.h>
#include <windows.h>
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"
//#include <winsock2.h>
//#include <ws2tcpip.h>
#include "CConsole.h"
#include "CConsoleLogger.h"
#include "CVanatime.h"
#include "mainloop.h"
#include "Utils/CWindowClass.h"
#include "Utils/T2List.h"
#include "Utils/T2DLinkedList.h"

CWindowClass* CConsole::outputWindow=NULL;

CSemaphore* CConsole::bufferCountSemaphore = NULL;
CSemaphore* CConsole::bufferMutex = NULL; 
T2List<CConsoleText*, CConsoleText*>* CConsole::textBuffer = NULL;

DWORD WINAPI CConsole::ConsolePrinter(LPVOID lpParam)
{
	HANDLE  hConsole;

	hConsole = GetStdHandle(STD_OUTPUT_HANDLE);	

	CConsoleText* textToPrint;
	while(true)
	{
		//Wait for something to print
		CConsole::bufferCountSemaphore->forceLock();

		//Only one reader, so no lock needed!
		bufferMutex->lock();
		textToPrint = CConsole::textBuffer->at(0);
		CConsole::textBuffer->removeAt(0);
		bufferMutex->unlock();

		if (textToPrint != NULL && textToPrint->timeStampText != NULL)
		{
			SetConsoleTextAttribute(hConsole,FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
			printf("%s", textToPrint->timeStampText);
			CConsoleLogger::Out(textToPrint->text, textToPrint->timeStampText);
		}
		else
			CConsoleLogger::Out(textToPrint->text);

		SetConsoleTextAttribute(hConsole, textToPrint->consoleAttribute);

		printf("%s\n", textToPrint->text);

		SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); //reset console

		//free memory
		delete textToPrint;
	}
}

CConsole::CConsole() 
{
}

CConsole::~CConsole()
{
}

CWindowClass* CConsole::getOutputWindow()
{
	if (outputWindow==NULL)
	{
		outputWindow = new CWindowClass();
		outputWindow->setWindowName("Server Information");
	}
	return outputWindow;
}

void CConsole::init() 
{
	if (CConsole::bufferCountSemaphore==NULL)
		CConsole::bufferCountSemaphore = new CSemaphore(0, false, false);

	if (CConsole::bufferMutex==NULL)
		CConsole::bufferMutex = new CSemaphore(1, false, false); 

	if (CConsole::textBuffer == NULL)
		CConsole::textBuffer = new T2DLinkedList<CConsoleText* ,CConsoleText*>;

	CreateThread(NULL, NULL, CConsole::ConsolePrinter, NULL, NULL, NULL);
}

void CConsole::pushToQueue(CConsoleText* textObject)
{
	//Push it into the queue
	CConsole::bufferMutex->lock(); 
	CConsole::textBuffer->pushBack(textObject, textObject);
	CConsole::bufferMutex->unlock(); 

	//Unlock the count semaphore, so the printer can continue its work
	CConsole::bufferCountSemaphore->forceUnlock();
}

void CConsole::outPlain(char* buf ,... )
{
	va_list ap;
	char text[4096];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);

	//Generate the CConsoleText object
	CConsoleText* textToPrint = new CConsoleText();
	textToPrint->text = new char[strlen(text)+1];

	//Fill it with the values
	strcpy(textToPrint->text, text);
	textToPrint->consoleAttribute = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;

	//Push it into the queue
	CConsole::pushToQueue(textToPrint);
}

void CConsole::outPlain(int color, char* buf ,... )
{
	va_list ap;
	char text[4096];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);

	//Generate the CConsoleText object
	CConsoleText* textToPrint = new CConsoleText();
	textToPrint->text = new char[strlen(text)+1];

	//Fill it with the values
	strcpy(textToPrint->text, text);
	textToPrint->consoleAttribute = color;

	//Push it into the queue
	CConsole::pushToQueue(textToPrint);
}

void CConsole::outTime(char* buf ,... )
{
	va_list ap;
	char text[4096];
	time_t rawtime;
	struct tm * timeinfo;
	char timestamp[100];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);

	time(&rawtime);
	timeinfo = localtime(&rawtime);
	strftime(timestamp, 100, "[%m/%d/%y %H:%M:%S]", timeinfo);

	//Generate the CConsoleText object
	CConsoleText* textToPrint = new CConsoleText();
	textToPrint->text = new char[strlen(text)+1];
	memset(textToPrint->text,0,strlen(text)+1);
	textToPrint->timeStampText = new char[strlen(timestamp)+1];

	//Fill it with the values
	strcpy(textToPrint->text, text);
	strcpy(textToPrint->timeStampText, timestamp);
	textToPrint->consoleAttribute = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;

	//Push it into the queue
	CConsole::pushToQueue(textToPrint);
}

void CConsole::outTime(int color, char* buf ,... )
{
	va_list ap;
	char text[4096];
	time_t rawtime;
	struct tm * timeinfo;
	char timestamp[100];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);

	time(&rawtime);
	timeinfo = localtime(&rawtime);
	strftime(timestamp, 100, "[%m/%d/%y %H:%M:%S]", timeinfo);

	//Generate the CConsoleText object
	CConsoleText* textToPrint = new CConsoleText();
	textToPrint->text = new char[strlen(text)+1];
	textToPrint->timeStampText = new char[strlen(timestamp)+1];

	//Fill it with the values
	strcpy(textToPrint->text, text);
	strcpy(textToPrint->timeStampText, timestamp);
	textToPrint->consoleAttribute = color;

	//Push it into the queue
	CConsole::pushToQueue(textToPrint);
}

void CConsole::outHr()
{
	CConsole::outPlain("===============================================================================");
}

void CConsole::outErr(char* buf, ...)
{
	va_list ap;
	char text[4096];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);
	
	CConsole::outTime( FOREGROUND_RED, text );
}

void CConsole::outDeb(char* buf, ...)
{
	va_list ap;
	char text[4096];

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);
	
	CConsole::outTime( FOREGROUND_GREEN | FOREGROUND_BLUE, text );
}

void CConsole::outDebOnly(char* buf, ...)
{
	#ifdef _DEBUG
	va_list ap;
	char text[4096];
	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);
	CConsole::outDeb(text);
	#endif
}

void CConsole::outHead(char* buf, ...){
	va_list ap;

	char  text[4096];	

	va_start(ap, buf);		// Parses The String For Variables
		vsprintf(text, buf, ap);	// And Converts Symbols To Actual Numbers
	va_end(ap);
	
	CConsole::outHr();
	CConsole::outPlain(text);
	CConsole::outHr();
}

int CConsole::recv(struct UDPdata * UDPcomm, struct Settings * s){
	switch(UDPcomm->command){
		case 99:
			UDPcomm->command = 0;
			UDPcomm->status = 0;
			return 1;
		case 98:
			s->logenabled = 1;
			break;
		case 97:
			s->logenabled = 0;
			break;
		case 96:
			s->filenum = 0;
			break;
	}
	UDPcomm->command = 0;
	return 0;
}

